package algo_annotations.data.pre_trt;

import algo_annotations.data.Data_a;
import algo_annotations.data.contrainte_superposition.GroupeSegmentFixe;
import algo_annotations.data.contrainte_superposition.SegmentFixe;
import algo_annotations.data.sites.GroupeSite;
import algo_annotations.data.sites.Site;
import algo_annotations.params.ParamsManager_a;
import algo_annotations.params.Params_a;
import algo_annotations.params.objects.ContrainteGeomSegment;
import algo_annotations.params.objects.ContrainteGeomAnno;
import commons.Constants;
import commons.data.EchelleGeneralization;
import commons.data.Model;
import commons.errors.ErrorsHandler;
import commons.io.in.Data_in;
import commons.io.in.Point_in;
import commons.io.in.Segment_in;
import commons.logs.Logger;
import commons.params.objects.ObjectColor;
import commons.data.Zone;
import commons.data.Zone.TypeZone;
import commons.data.category.Category;
import commons.io.in.IObjetGeo_in;
import commons.params_appli.ParamsAppli;
import commons.spatialindex.simplegrid.SimpleGridSPI;
import commons.utils.UtilsCalcul;
import commons.utils.UtilsGeom;
import commons.utils.UtilsTime;
import commons.utils.primitive_geom.Point_d;
import commons.utils.primitive_geom.Rect_d;
import java.awt.Color;
import java.util.ArrayList;
import javax.swing.JFrame;


public class PreTraitementInputData_a {

	static private Params_a params;

	static private GroupeSegmentFixe grpSeg;
	static private GroupeSite grpSite;

	// qques statistiques
	static private int nbSite_init;
	static private int nbSite_afterMainfilter;
	static private int nbRealAnno_apresAffectationEchelle;
	static private int nbRealAnno_apresSupprDoublons;

	static private int nbSegment_init;
	static private int nbSegment_superposes;
	static private int nbSegment_afterMainfilter;
	static private int nbSegment_afterAffectationEchelle;
	static private int nbSegment_afterOptimization;

	private static int nbIntersect_ZoneSeg_in;
	private static int nbSegment_apresdecoupIntersection;
	private static int nbSite_afterZoneFilter;

	// Utilisé notamment pour la suppression des doublons de sites
	static SimpleGridSPI spiSite;
	// Utilisé notamment pour accélérer les calculs d'intersections de segments
	static SimpleGridSPI spiSeg;


	// Aide le GC
	static private void clear() {
		if (spiSeg != null)
			spiSeg.clear();
		if (spiSite != null)
			spiSite.clear();
		spiSeg = null;
		spiSite = null;
	}

	public static Data_a createDataFromDirectory(JFrame frameParent, String path_RepFichierSource) {

		Data_in data_in = null;
		Data_a data=null;

		while (data == null) {
			ErrorsHandler.reinitErrors();

			//**********************************************************************
			// A) Lecture des données dans le répertoire   Répertoire --> Data_in
			// on doit relire après chaque essai des paramètres car les segments ont
			//  peut etre déjà été découpé, par ex. : il vaut mieux repartir de zero
			//  meme si c'est plus long
			Logger.log_INFO1("");
			Logger.log_INFO1("========================================");
			Logger.log_INFO1("==== Lecture des fichiers en entrée ====");
			Logger.log_INFO1(" répertoire = " + path_RepFichierSource);
			UtilsTime.startTimer("Lecture fichier");

			if (data_in != null) data_in.clear(); // ceci aide grandement le GC qui fait un memory leaks si trop de boucles !!
			System.gc();

			data_in = Data_in.readFromDirectory_dontLoadData(path_RepFichierSource);
			
			UtilsTime.stopTimer("Lecture fichier");
			if (ErrorsHandler.isThereErrors()) {
				ErrorsHandler.addError("Impossible de lire les fichiers du répertoire : " + path_RepFichierSource);
				return null;
			}

			//**********************************************************************
			// B) Paramétrage de l'algorithme
			//    en entrée, les couches lues dans le répertoire de travail
			Logger.log_INFO1("");
			Logger.log_INFO1("=========================================");
			Logger.log_INFO1("==== Paramétrage de l'algorithme     ====");
			params = (Params_a) new ParamsManager_a(frameParent, data_in).ouvreDialogParametrage();

			// Erreurs possibles sur les fichiers de Font, notamment
			if (ErrorsHandler.isThereErrors()) {
				ErrorsHandler.addError("Impossible de constituer les paramètres du traitement");
				ErrorsHandler.showErrors();
				ErrorsHandler.reinitErrors();
				return null;
			}

			if (params==null) {
				// Annulation du paramétrage
				Logger.log_INFO1("");
				Logger.log_INFO1("~~~~~~ PARAMETRAGE ANNULE PAR L'UTILISATEUR ~~~~~~");

				data_in.clear();
				data_in = null;
				System.gc();

				break;
			}

			// A la sortie du paramétrage, il est temps de finir de charger les données dans les données
			// (seul le début des fichiers a été lu pour le beosin du paramétrage)
			// on ne lit que les 2 couches qui ont été demandées en paramétrage
			Logger.log_INFO1("Lecture des données géographiques des fichiers");
			UtilsTime.startTimer("Lecture donnees fichier");
			data_in.getCouche(params.nomCoucheSite).readData();
			data_in.getCouche(params.nomCoucheSegement).readData();
			UtilsTime.stopTimer("Lecture donnees fichier");
			if (ErrorsHandler.isThereErrors()) {
				data_in.clear();
				data_in = null;
				// on sort, fichier erroné
				return null;
			}
			
			// Limitation éventuelle du logiciel
			if (ParamsAppli.hasLimitationForAnno && params.coucheSite.getNbObject()>ParamsAppli.maxNbNodes) {
				//JOptionPane.showMessageDialog(null, "Cette version d'Autogene est limitée à " + ParamsAppli.maxNbNodes + " au maximum");
				ErrorsHandler.addError("Cette version d'Autogene est limitée à " + ParamsAppli.maxNbNodes + " noeuds au maximum");
				return null;
			}

			//**********************************************************************
			// C) Traitement des données d'entrée = création d'un objet Data_a qui
			//     contienda toutes les données nécessaires aux traitements ultérieurs
			UtilsTime.startTimer("TOTAL PRE-TRAITEMENT DONNEES");
			data = traiteInputData (data_in);
			UtilsTime.stopTimer("TOTAL PRE-TRAITEMENT DONNEES");
			
			if (ErrorsHandler.isThereErrors()) {
				ErrorsHandler.addError("Impossible de réaliser le pré-traitement des données d'entrée");
				ErrorsHandler.showErrors();
				ErrorsHandler.printErrors();
				// et on reboucle
			} else
				// tout est OK, on sort de la boucle
				break;

		}

		// si l'utilisateur n'a pas annulé
		if (params != null)
			printStatsPreTraitement(data_in, data);

		clear();

		return data;
	}


	// Renvoie null si erreur
	private static Data_a traiteInputData(Data_in data_in) {
		try {
		Logger.log_INFO1("");
		Logger.log_INFO1("=========================================");
		Logger.log_INFO1("==== PRE-TRAITEMENT DONNEES D'ENTREE ====");
		
		spiSeg = new SimpleGridSPI("Index Segments", params.coucheSegment.getEmprise(), SimpleGridSPI.getTailleFor(params.coucheSegment.getNbObject()));
		spiSite = new SimpleGridSPI("Index Sites", params.coucheSite.getEmprise(), SimpleGridSPI.getTailleFor(params.coucheSite.getNbObject()));

		Data_a data = new Data_a(params);

		//***** Pretraitement **************************************************
		// On complète le modèle des sites avec des infos utiles que l'algo va écrire lors de l'export
		completeModeleSite();
		if (ErrorsHandler.isThereErrors())
			return null;


		//**********************************************************************
		// Affectation des catégories aux sites et aux segments
		//  -> les catégories sont stockées dans leur Attributes
		UtilsTime.startTimer("Affectation catégories et filtrage principal");
		affecteCategories_and_MainFilter();
		UtilsTime.stopTimer("Affectation catégories et filtrage principal");

		// A l'issue du filtre principal, test si il y a effectivement des annotations à généraliser
		// (les segments ne sont pas obligatoires)
		if (params.coucheSite.getNbObject()==0) {
			ErrorsHandler.addError("Après application du filtre principal, il n'y a aucune annotation à généraliser");
			return null;
		}


		//**********************************************************************
		// Pre-traitement des segments
		// l'utilisateur a-t-il définit une couche de segments à éviter ?
		if (params.coucheSegment != null) {
			ArrayList<Segment_in> vSeg_in = (ArrayList) params.coucheSegment.getObjects();

			// Suppression des éventuels segments superposés
			UtilsTime.startTimer("Verif segments superposes");
			checkSegmentsSuperposes();
			UtilsTime.stopTimer("Verif segments superposes");

			// Calcul des intersections entre les segments et les zones
			UtilsTime.startTimer("Calcul intersect");
			computeIntersect_SegZone();
			UtilsTime.stopTimer("Calcul intersect");

			// Découpage effectif des segments
			UtilsTime.startTimer("Découpage segments");
			realiseDecoupageIntersectionSegment ();
			UtilsTime.stopTimer("Découpage segments");
		}


		//**********************************************************************
		// Affectation des zones aux Segment et aux Site
		UtilsTime.startTimer("Affectation zones");
		affecteZones();
		UtilsTime.stopTimer("Affectation zones");

		//**********************************************************************
		// Affectation des contraintes geom, echelle de généralisation et couleur aux segment et aux sites
		UtilsTime.startTimer("Affectation contraintes geom etc. aux Segment et Point");
		affecte_ContraintesGeom_RegleGeneralisation_ParamAffichage_toSegmentSite();
		UtilsTime.stopTimer("Affectation contraintes geom etc. aux Segment et Point");
		if (ErrorsHandler.isThereErrors())
			return null;



		UtilsTime.startTimer("Filtrage objets selon zone");
		filtreSegmentSite_notToBeGeneralized();
		UtilsTime.stopTimer("Filtrage objets selon zone");


		// A ce stade, il se peut qu'il n'y ait aucun site à généraliser (= mauvais paramétrage ...)
		if (params.coucheSite.getNbObject() == 0) {
			ErrorsHandler.addError("Après application des échelles, il n'y a aucune annotation à généraliser !! (que des échelles = 0)");
			return null;
		}

		//**********************************************************************
		// Transformation des Point_in en Site, objet final utilisé par l'algo
		ArrayList<Point_in> vSites_in = (ArrayList) data_in.getCouche(params.nomCoucheSite).getObjects();
		grpSite = new GroupeSite(params);

		for (Point_in site_in : vSites_in) {
			Site s = new Site(data, site_in, params);
			grpSite.addSite(s);
		}


		//**********************************************************************
		// Transformation des Segment_in en SegmentFixe, objet final utilisé par l'algo
		grpSeg = new GroupeSegmentFixe(data);
		if (params.coucheSegment != null) {
			ArrayList<Segment_in> vSeg_in = (ArrayList) params.coucheSegment.getObjects();
			for (Segment_in seg_in : vSeg_in) {
				SegmentFixe seg = new SegmentFixe(seg_in, params);
				grpSeg.addSegment(seg);
			}
		}


		// Suppression des doublons d'anno (éventuellement, si demandés dans les params par l'utilisateur)
		supprDoublons();


		// A l'issue de tous ces traitements, les différents objets se sont vus affecter des informations.
		// Si les paramètres étaient mauvais (notamment au niveau des définitions de
		//  catégories), certains objets se retrouvent sans  contraintes géométrique,
		//  sans règles de généralisation etc.
		checkData();
		if (ErrorsHandler.isThereErrors()){
			return null;
		}


		// Optimisation Segments
		Logger.log_INFO1("-- Optimisation des segments --");
		UtilsTime.startTimer("Optimisation segments");
		if(grpSeg.vSegment.isEmpty()) {
			Logger.log_INFO1("  -> aucun segment à traiter");
			nbSegment_afterOptimization = 0;
		} else {
			grpSeg.optimze(params);
			nbSegment_afterOptimization = grpSeg.vSegment_optimise.size();			
		}
		UtilsTime.stopTimer("Optimisation segments");

		// Optimisation Sites : precalculs
		Logger.log_INFO1("-- Optimisation des sites --");
		UtilsTime.startTimer("Optimisation sites");
		grpSite.optimize_precalculProximite(grpSeg);
		UtilsTime.stopTimer("Optimisation sites");
		
		// J) Optimisation Sites : découpage du probleme pour la 1ere passe
		Logger.log_INFO1("-- Sous découpage problème --");
		UtilsTime.startTimer("Sous decoupage probleme");
		grpSite.prepareTraitement_pass1(grpSeg, params);
		UtilsTime.stopTimer("Sous decoupage probleme");

		
		data.initData(grpSite, grpSeg);

		return data;

		} catch(StackOverflowError t) {
			// possible lors de la phase de découpage, car pas mal de récursions
			ErrorsHandler.addError("Exception StackOverflow durant le prétraitement : relancer l'application avec un paramètre -Xss plus élevé");
			return null;
		} catch(OutOfMemoryError t) {
			// normalement, pas de souci avec cette exception durant la phase de prétraitement ...
			ErrorsHandler.addError("Exception OutOfMemory durant le prétraitement : relancer l'application avec des paramètres -Xms et -Xmx plus élevés");
			return null;
		}
	}


	private static void completeModeleSite() {
		Model model = params.coucheSite.getModel();

		model.registerField(Constants.ANNO_FIELD_ECHELLE, Model.TypeField.INT, 4);
		model.registerField(Constants.ANNO_FIELD_NOTE_GLOBALE, Model.TypeField.DBL, 8);
		model.registerField(Constants.ANNO_FIELD_NOTE_AMBIG, Model.TypeField.DBL, 8);
		model.registerField(Constants.ANNO_FIELD_IS_MULTILIGNE, Model.TypeField.INT, 4);

		// Maintenant que le modèle a grossi, il faut indiquer à chaque site que son attributes doit pouvoir recueillir ces valeurs en plus
		for (IObjetGeo_in site : params.coucheSite.getObjects())
			site.getAttributes_in().addAttributes(4);
	}

	
	static private void affecteCategories_and_MainFilter() {
		Logger.log_INFO1("-- Affectation des catégories et filtrage principal --");

		//********* D'abord les sites ******************************************
		params.coucheSite.affecteCategories(params.vCategorySite);
		// nb d'objets AVANT filtre principal
		nbSite_init = params.coucheSite.getNbObject();
		// Filtrage proprement dit
		params.coucheSite.filtreObjets_mainFilter(params.mainFilterSite);
		// nb d'objets APRES filtre principal
		nbSite_afterMainfilter= params.coucheSite.getNbObject();

		//********* Puis les segments ******************************************
		if (params.coucheSegment != null) {
			params.coucheSegment.affecteCategories(params.vCategorySegment);
			// nb d'objets AVANT filtre principal
			nbSegment_init = params.coucheSegment.getNbObject();
			// Filtrage proprement dit
			params.coucheSegment.filtreObjets_mainFilter(params.mainFilterSegment);
			// nb d'objets APRES filtre principal
			nbSegment_afterMainfilter= params.coucheSegment.getNbObject();
		}
	}


	static private void checkSegmentsSuperposes() {
		Logger.log_INFO1("-- Vérification segments superposés --");
		nbSegment_superposes = 0;
		ArrayList<Segment_in> vSegment = (ArrayList) params.coucheSegment.getObjects();

		spiSeg.clear();

		int nbSeg = vSegment.size();
		for (int i = 0; i < nbSeg; i++) {
			Segment_in seg = vSegment.get(i);
			if (spiSeg.isThisSegmentInSPI(seg)) {
				Logger.log_WARN("Segment superposé " + seg);
				nbSegment_superposes++;
			} else
				spiSeg.addObject(seg);
		}

		if (nbSegment_superposes > 0) {
			Logger.log_WARN(" => en tout, " + nbSegment_superposes + " segments superposés --> supprimés");

			// On remplace les objets avec ceux, uniques, du SPI
			params.coucheSegment.getObjects().clear();
			params.coucheSegment.getObjects().addAll((ArrayList)spiSeg.getAllObjects());
		}
	}

	
	// Calcule les pt d'intersection entre segments et les stocke dans les Segment_in, sans les découper
	static private void computeIntersect_SegZone() {
		Logger.log_INFO1("-- Calcul intersection Zones/Segment --");
		ArrayList<Segment_in> vSegment = (ArrayList) params.coucheSegment.getObjects();

		spiSeg.clear();
		spiSeg.addObjects (vSegment);


		nbIntersect_ZoneSeg_in = 0;
		double[] tabCoord = new double[4];
		// Pour chaque zone, on parcourt les segments alentours
		for (Zone zone : params.vZone) {
			if (zone.isZonePrincipale()) continue; // pas d'intersection possible avec la zone principale

			ArrayList<Segment_in> vSegProches = (ArrayList) spiSeg.getObjectsThatMayIntersectWith(zone);

			for (Segment_in seg : vSegProches) {
				int nbIntersect=0;
				if (zone.typeZone == TypeZone.RECT)
					nbIntersect = UtilsGeom.computeIntersect_Rect_Segment(zone, seg, tabCoord);
				else if (zone.typeZone == TypeZone.ROND)
					nbIntersect = UtilsGeom.computeIntersect_Ellipse_Segment(zone, seg, tabCoord);
				else assert false;

				if (nbIntersect >= 1)
					seg.addPtIntersection(new Point_d(tabCoord[0], tabCoord[1]));
				if (nbIntersect == 2)
					seg.addPtIntersection(new Point_d(tabCoord[2], tabCoord[3]));

				nbIntersect_ZoneSeg_in += nbIntersect;
			}
		}
	}

	
	// Une fois les intersection precalculée, on découpe
	private static void realiseDecoupageIntersectionSegment() {
		Logger.log_INFO1("-- Decoupage des Segments --");
		// Comme certains segments vont être découpés, donc devant être supprimés
		//  du vecteur de segment du Data_in, il est plus rapide de recréer
		//  un nouveaux vecteur de segment que l'on va alimenter progressivement
		//  et on fera l'echange dans le File_in
		ArrayList vSegmentIn = params.coucheSegment.getObjects();
		ArrayList vSegmentDecoupe = new ArrayList<Segment_in>(vSegmentIn.size());
		for (int i = 0; i < vSegmentIn.size(); i++) {
			Segment_in seg = (Segment_in) vSegmentIn.get(i);
			if (seg.getNbPtIntersection()==0) {
				// pas besoin de découper, on enregistre le seg directement
				vSegmentDecoupe.add(seg);
			} else {
				vSegmentDecoupe.addAll(seg.decoupe());
			}
		}
		// petite permutation de vecteurs
		params.coucheSegment.setVObj(vSegmentDecoupe);

		// nb de segment APRES découpage
		nbSegment_apresdecoupIntersection = params.coucheSegment.getNbObject();
	}


	/**
	 * Affectation des zones aux sites et aux segments.
	 * L'affectation de zone sert a pouvoir, dans la suite du traitement
	 *  - ne pas fusionner des segments de zones différentes pour la simplification DouglasPeuker
	 *    meme s'ils ont les memes caractéristiques (épaisseur)
	 *  - affecter des paramètres de généralisation spécifique aux éléments
	 * Pour rappel, tous les objets font automatiquement partie de la zone principale par défaut
	 * car la fonction contientPt_larg() de la zone principale renvoie toujours true ...
	 */
	private static void affecteZones() {

		Logger.log_INFO1("-- Affectation des zones aux sites --");
		ArrayList<Point_in> vSite_in = (ArrayList)params.coucheSite.getObjects();
		for (Point_in site_in : vSite_in)
			for (Zone zone : params.vZone)
				if (zone.contientPt_larg(site_in))
					site_in.getZones().addZone(zone);

		if (params.coucheSegment==null) return;
		Logger.log_INFO1("-- Affectation des zones aux segments --");
		ArrayList<Segment_in> vSeg_in = (ArrayList)params.coucheSegment.getObjects();
		for (Segment_in seg_in : vSeg_in) {
			Point_d centerSeg = seg_in.getCenter();
			for (Zone zone : params.vZone)
				// Pour les segments, le test de présence dans la zone se fait avec le point milieu
				//  car, rappel, les segments ont déjà été découpés selon la bordure des zones
				if (zone.contientPt_larg(centerSeg))
					seg_in.getZones().addZone(zone);
		}
	}



	//Affectation des contraintes Geom au segment_in (il faut avoir affecter les zones auparavant)
	static private void affecte_ContraintesGeom_RegleGeneralisation_ParamAffichage_toSegmentSite() {
		Logger.log_INFO1("-- Affectation des contraintes geométrique aux segment d'entree --");

		//***********************************************************************************************************
		// TRAITEMENT DES SITES
		ArrayList<Point_in> vSite = (ArrayList) params.coucheSite.getObjects();
		int nbSite = vSite.size();
		for (int i = 0; i < nbSite; i++) {
			Point_in site = vSite.get(i);

			// 1) Contraintes géométriques
			for (ContrainteGeomAnno cga : params.vContrainteGeomAnno)
				if (site.attributes_in.isOfCategory(cga.categorySite))
					site.contrainteGeomAnno = cga;
			if (site.contrainteGeomAnno == null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des contraintes géométriques des Sites");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(site.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Ce Site n'a pas de contrainte géométrique (épaisseur, etc.) : " + site);
				return;
			}


			// 2) Echelles de généralisation
			// rappel : une Echelle de Generalisation consiste en une echelle particulière
			//          pour une certaine catégorie d'objet dans une certaine zone
			// La 1ere Echelle de generalisation est toujours une regle qui s'applique à tous les objets dans toutes les zones (ParamsManager s'en charge ...)
			// Au final, on est certain que tout objet aura une echelle de généralisation
			assert(params.vEchelleGeneralizationSite.get(0).zone == Zone.zonePrincipale && params.vEchelleGeneralizationSite.get(0).category == Category.categoryAllData);
			for (EchelleGeneralization eg : params.vEchelleGeneralizationSite)
				if (site.attributes_in.isOfCategory(eg.category) && site.zones.isOfZone(eg.zone))
					site.echelleGeneralization = eg;
			assert (site.echelleGeneralization != null); // ne depend pas des params utilisateur, erreur impossible


			// 3) Couleurs pour la differentiation visuelle
			if (params.bUseDifferentiationCouleur) {
				for (ObjectColor col : params.vObjectColor) {
					if (site.attributes_in.isOfCategory(col.category))
						site.color = col.color;
				}
			} else {
				// pas de differentiation visuelle : pour l'illustrer visuellement
				// on force la couleur noire, meme si la table de parametrage contenait des definition de couleur
				site.color = Color.black;
			}

			if (site.color==null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des couleurs des sites");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(site.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Ce Site n'a pas de couleur : " + site);
				return;
			}
		}

		//***********************************************************************************************************
		// TRAITEMENT DES SEGMENTS
		int nbContrainteGeomLine = params.vContrainteGeomSegment.size();
		int nbEchelleGeneralisation = params.vEchelleGeneralizationSegment.size();

		ArrayList<Segment_in> vSegment = (ArrayList) params.coucheSegment.getObjects();
		int nbSeg = vSegment.size();

		for (int i = 0; i < nbSeg; i++) {
			Segment_in seg = vSegment.get(i);

			// 1) Contraintes géométriques
			//    On affectre dans l'ordre : la dernière contrainte l'emporte

			for (int j=0 ; j<nbContrainteGeomLine ; j++) {
				ContrainteGeomSegment cgs = params.vContrainteGeomSegment.get(j);
				if (seg.attributes_in.isOfCategory(cgs.categorySegment))
					seg.contrainteGeomSegment = cgs;
			}
			if (seg.contrainteGeomSegment == null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des contraintes géométriques des segments");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(seg.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Ce Segment n'a pas de contrainte géométrique (épaisseur, etc.) : " + seg);
				return;
			}

			// 2) Echelle de généralisation
			//    Rappel : une Echelle de Generalisation consiste en une echelle particulière
			//          pour une certaine catégorie d'objet dans une certaine zone
			//    On affecte dans l'ordre : la dernière règle l'emporte
			// La 1ere Echelle de generalisation est toujours une regle qui s'applique à tous les objets dans toutes les zones (ParamsManager s'en charge ...)
			// Au final, on est certain que tout objet aura une echelle de généralisation
			assert(params.vEchelleGeneralizationSegment.get(0).zone == Zone.zonePrincipale && params.vEchelleGeneralizationSegment.get(0).category == Category.categoryAllData);
			for (int j=0 ; j<nbEchelleGeneralisation ; j++) {
				EchelleGeneralization eg = params.vEchelleGeneralizationSegment.get(j);
				if (seg.attributes_in.isOfCategory(eg.category) && seg.zones.isOfZone(eg.zone))
					seg.echelleGeneralization = eg;
			}
			assert (seg.echelleGeneralization != null); // ne depend pas des params utilisateur, erreur impossible

			// Pas de couleur pour les segments ...
		}
	}


//	private static void affecte_ContraintesGeom_EchelleGeneralisation_ParamAffichage() {
//		Logger.log_INFO1("-- Affectation des contraintes géométriques, échelles de généralisation et paramètres d'affichage --");
//		//********** TRAITEMENT DES SITES **************************************
//		for (Site site : grpSite.vSite) {
//
//			// 1) Contraintes géométriques (= epaisseur)
//			//    On affecte dans l'ordre : la dernière contrainte l'emporte
//			for (ContrainteGeomAnno pga : params.vContrainteGeomAnno)
//				if (site.getAttributes().isOfCategory(pga.categorySite))
//					site.setParamGeom (pga);
//
//			// 2) Echelle de généralisation
//			//    Rappel : une Regle de Generalisation consiste en une echelle particulière
//			//          pour une certaine catégorie d'objet dans une certaine zone
//			//    On affecte dans l'ordre : la dernière règle l'emporte
//			// La 1ere regle de generalisation est toujours une regle qui s'applique à tous les objets dans toutes les zones (ParamsManager s'en charge ...)
//			// Au final, on est certain que tout objet aura une echelle de généralisation
//			assert(params.vEchelleGeneralizationSite.get(0).zone == Zone.zonePrincipale && params.vEchelleGeneralizationSite.get(0).category == Category.categoryAllData);
//			site.setEchelleGeneralization(params.getDefautEchelleGeneralizationSite());
//			for (EchelleGeneralization eg : params.vEchelleGeneralizationSite)
//				if (site.getAttributes().isOfCategory(eg.category) && site.getZones().isOfZone(eg.zone))
//					site.setEchelleGeneralization(eg);
//
//			// 3) Couleurs pour la differentiation visuelle
//			if (params.bUseDifferentiationCouleur) {
//				for (ObjectColor col : params.vObjectColor) {
//					if (site.getAttributes().isOfCategory(col.category))
//						site.setColor(col.color);
//				}
//			} else {
//				// pas de differentiation visuelle : pour l'illustrer visuellement
//				// on force la couleur noire, meme si la table de parametrage contenait des definition de couleur
//				site.setColor(Color.black);
//			}
//		}
//
//		nbRealAnno_apresAffectationEchelle = grpSite.getNbAnnoToBeGeneralized();
//
//
//		//********** TRAITEMENT DES SEGMENTS ***********************************
//		for (SegmentFixe seg : grpSeg.vSegment) {
//
//			// 1) Contraintes géométriques (= epaisseur)
//			//    On affecte dans l'ordre : la dernière contrainte l'emporte
//			for (ContrainteGeomSegment cgs : params.vContrainteGeomSegment)
//				if (seg.getAttributes().isOfCategory(cgs.categorySegment))
//					seg.setContrainteGeom (cgs);
//
//			// 2) Echelle de généralisation
//			//    Rappel : une Echelle de Generalisation consiste en une echelle particulière
//			//          pour une certaine catégorie d'objet dans une certaine zone
//			//    On affecte dans l'ordre : la dernière règle l'emporte
//			// La 1ere regle de generalisation est toujours une regle qui s'applique à tous les objets dans toutes les zones (ParamsManager s'en charge ...)
//			// Au final, on est certain que tout objet aura une echelle de généralisation
//			assert(params.vEchelleGeneralizationSegment.get(0).zone == Zone.zonePrincipale && params.vEchelleGeneralizationSegment.get(0).category == Category.categoryAllData);
//			for (EchelleGeneralization eg : params.vEchelleGeneralizationSegment)
//				if (seg.getAttributes().isOfCategory(eg.category) && seg.getZones().isOfZone(eg.zone))
//					seg.setEchelleGeneralization(eg);
//			assert (seg.getEchelleGeneralization() != null);
//		}
//
//	}

	/**
	 * Les segments et nodes ont leur zone, on va pouvoir déterminer ceux qui n'ont
	 * pas besoin d'etre généralisés (échelle de généralisation = 0)
	 */
//	private static void filtreSiteSegment_parZone() {
//		Logger.log_INFO1("-- Filtrage des sites et des segments par zone --");
//
//		params.coucheSite.filtreObjets_notToBeGeneralized(params.vEchelleGeneralizationSite);
//		// nb d'objets APRES filtre par zones
//		nbSite_afterZoneFilter = params.coucheSite.getNbObject();
//
//		if (params.coucheSegment==null) return;
//		params.coucheSegment.filtreObjets_notToBeGeneralized(params.vEchelleGeneralizationSegment);
//		// nb d'objets APRES filtre par zones
//		nbSegment_afterZoneFilter = params.coucheSegment.getNbObject();
//	}

	/**
	 * Les segments et nodes ont leur zone et leur échelle de généralisation, on va pouvoir déterminer ceux qui n'ont
	 * pas besoin d'etre généralisés (échelle de généralisation = 0)
	 */
	private static void filtreSegmentSite_notToBeGeneralized() {
		Logger.log_INFO1("-- Filtrage des segments et sites a ne pas generaliser --");

		// Sites
		ArrayList<Point_in> vSite_origine = (ArrayList<Point_in>) params.coucheSite.getObjects();
		ArrayList<Point_in> vSite_toBeGeneralized = new ArrayList<Point_in>(vSite_origine.size());

		int nbSite = vSite_origine.size();
		for (int i=0; i<nbSite; i++) {
			Point_in site = vSite_origine.get(i);
			if (site.echelleGeneralization.bToBeGeneralized)
				vSite_toBeGeneralized.add(site);
		}
		params.coucheSite.setVObj(vSite_toBeGeneralized);


		// Segments
		ArrayList<Segment_in> vSegment_origine = (ArrayList<Segment_in>) params.coucheSegment.getObjects();
		ArrayList<Segment_in> vSegment_toBeGeneralized = new ArrayList<Segment_in>(vSegment_origine.size());

		int nbSeg = vSegment_origine.size();
		for (int i=0; i<nbSeg; i++) {
			Segment_in seg = vSegment_origine.get(i);
			if (seg.echelleGeneralization.bToBeGeneralized)
				vSegment_toBeGeneralized.add(seg);
		}
		params.coucheSegment.setVObj(vSegment_toBeGeneralized);


		// nb d'objets APRES filtre sur l'échelle de généralisation
		nbSegment_afterAffectationEchelle = params.coucheSegment.getNbObject();
		nbRealAnno_apresAffectationEchelle = params.coucheSite.getNbObject();
	}


	private static void supprDoublons() {
		if (!params.bSupprDoublon) return;
		Logger.log_INFO1("-- Suppression des doublons d'annotation --");

		spiSite.clear();
		spiSite.addObjects (grpSite.vSite);

		for (Site site : grpSite.vSite) {
			if (!site.hasAnno()) continue;

			double distMax = site.hauteurAnno_1L_reel_m * params.factorDistMaxSupprDoublons;

			Rect_d rectSearch = new Rect_d(site, distMax*2);
			ArrayList<Site> vSitesProches = (ArrayList) spiSite.getObjectsThatMayIntersectWith(rectSearch);
			for (Site s : vSitesProches) {
				if (s==site) continue;
				if (site.distTo(s) <= distMax && site.libelle.equals(s.libelle) && site.getColor().equals(s.getColor()) && site.hauteurAnno_1L_reel_m==s.hauteurAnno_1L_reel_m) {
					Logger.log_INFO2(" -> Suppression de l'annotation : " + s.libelle);
					// on a trouvé un site suffisament proche, de libelle et de représentation identique : on supprime son libelle
					//  -> le site restera (pour test superposition avec d'autres annos, mais il n'aura pas d'anno
					s.libelle = "";
				}
			}
		}

		nbRealAnno_apresSupprDoublons = grpSite.getNbAnnoToBeGeneralized();
	}


	private static void checkData() {
		//---- TRAITEMENT DES SITE ---------------------------------------------
		for (Site site : grpSite.vSite) {
			// Test 1 : tous les site doivent avoir une contrainte geométrique
			if (site.getContrainteGeom()==null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des paramètres géométriques des annotations");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(site.getAttributes().toString_only10Values(), true);
				ErrorsHandler.addError("Cette annotation n'a pas de paramètre géométrique (hauteur anno, etc.) : " + site);
				return;
			}
			// Test 2 : tous les sites doivent avoir une règle de généralisation
			//  l'erreur est normalement impossible vu qu'il y a la zone principale qui est toujours active ...
			if (site.getEchelleGeneralization()==null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des échelles de généralisation des annotations");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(site.getAttributes().toString_only10Values(), true);
				ErrorsHandler.addError("Cette annotation n'a pas d'échelle de généralisation : " + site);
				return;
			}
			// Test 3 : si l'option de differentiation visuelle a été choisie, il faut que chaque site ait une couleur
			if (params.bUseDifferentiationCouleur) {
				if (site.getColor() == null) {
					ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des couleurs des annotations");
					if (Logger.isDebugLevel()) ErrorsHandler.addError(site.getAttributes().toString_only10Values(), true);
					ErrorsHandler.addError("Cette annotation n'a pas de couleur (alors que l'option de differentiation par les couleur a été choisie) : " + site);
					return;
				}
			}
		}

		// Il faut qu'il y ait au moins un site avec anno dont le texte est non vide et d'échelle >0 ...
		if (grpSite.getNbAnnoToBeGeneralized() == 0) {
			ErrorsHandler.addError("Il n'y a aucune annotation à généraliser (pas d'anno ou que des libellés vides");
			return;
		}


		//---- TRAITEMENT DES SEGMENTS ---------------------------------------------
		for (SegmentFixe seg : grpSeg.vSegment) {
			// Test 1 : tous les segments doivent avoir une contrainte geométrique
			if (seg.getContrainteGeom()==null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des contraintes géométriques des segments à éviter");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(seg.getAttributes().toString_only10Values(), true);
				ErrorsHandler.addError("Ce Segment n'a pas de contrainte géométrique (hauteur anno, etc.) : " + seg);
				return;
			}
			// Test 2 : tous les segments doivent avoir une règle de généralisation
			//  l'erreur est normalement impossible vu qu'il y a la zone principale qui est toujours active ...
			if (seg.getEchelleGeneralization()==null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des règles de généralisation des segments à éviter");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(seg.getAttributes().toString_only10Values(), true);
				ErrorsHandler.addError("Ce Segment n'a pas de règle de généralisation (zone+échelle) : " + seg);
				return;
			}
		}
	}


	private static void printStatsPreTraitement(Data_in data_in, Data_a data) {
		Logger.log_INFO1("");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("** Statistiques du pre-traitement des données d'entrée **");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1(data_in.path_RepFichierSource);
		
		Logger.log_INFO1("");
		data_in.printStatsGlobales();

		Logger.log_INFO2("");
		Logger.log_INFO2("-- Préparation des données :");
		Logger.log_INFO2(" nb. sites dans fichier d'entrée           : " + nbSite_init);
		Logger.log_INFO2(" -> nb. sites après filtre principal       : " + nbSite_afterMainfilter);
		Logger.log_INFO2(" -> nb. anno après affectation échelles    : " + nbRealAnno_apresAffectationEchelle); // on ne compte plus les libelle = "" ...

		if (params.bSupprDoublon)
		Logger.log_INFO2(" -> nb. anno après suppression doublons    : " + nbRealAnno_apresSupprDoublons);

		Logger.log_INFO2(" nb. objets lineaires dans fichier d'entrée: " + params.coucheSegment.getNbAttributes());
		Logger.log_INFO2(" -> nb. segments dans fichier d'entrée     : " + nbSegment_init);
		Logger.log_INFO2(" -> nb. segments après filtre principal    : " + nbSegment_afterMainfilter);
		Logger.log_INFO2(" -> nb. segments après affectation échelle : " + nbSegment_afterAffectationEchelle);
		Logger.log_INFO2(" -> nb. segments après optim DouglasPeuck. : " + nbSegment_afterOptimization);

		if (data!=null) {
		Logger.log_DEBUG1("");
		Logger.log_DEBUG1("==== RAPPORT d'optimisation ====");
		Logger.log_DEBUG1(" => PRECALCULS SITES : ");
		Logger.log_DEBUG1("  moy seg intersectable par site FULL  : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_FULL));
		Logger.log_DEBUG1("  moy seg intersectable par site RIGHT : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_RIGHT));
		Logger.log_DEBUG1("  moy seg intersectable par site LEFT  : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_LEFT));
		Logger.log_DEBUG1("  moy seg intersectable par site UP    : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_UP));
		Logger.log_DEBUG1("  moy seg intersectable par site DOWN  : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_DOWN));
		Logger.log_DEBUG1("  moy seg intersectable par site UP-RIGHT   : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_UPRIGHT));
		Logger.log_DEBUG1("  moy seg intersectable par site UP-LEFT    : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_UPLEFT));
		Logger.log_DEBUG1("  moy seg intersectable par site DOWN-RIGHT : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_DOWNRIGHT));
		Logger.log_DEBUG1("  moy seg intersectable par site DOWN-LEFT  : " + data.grpSite.getMeanPerSite_NbSegmentIntersectable(Site.SSRECT_DOWNLEFT));
		Logger.log_DEBUG1("  moy site intersect lien FULL  : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_FULL));
		Logger.log_DEBUG1("  moy site intersect lien RIGHT : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_RIGHT));
		Logger.log_DEBUG1("  moy site intersect lien LEFT  : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_LEFT));
		Logger.log_DEBUG1("  moy site intersect lien UP    : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_UP));
		Logger.log_DEBUG1("  moy site intersect lien DOWN  : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_DOWN));
		Logger.log_DEBUG1("  moy site intersect lien UP-RIGHT   : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_UPRIGHT));
		Logger.log_DEBUG1("  moy site intersect lien UP-LEFT    : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_UPLEFT));
		Logger.log_DEBUG1("  moy site intersect lien DOWN-RIGHT : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_DOWNRIGHT));
		Logger.log_DEBUG1("  moy site intersect lien DOWN-LEFT  : " + data.grpSite.getMeanPerSite_NbSiteIntersectLien(Site.SSRECT_DOWNLEFT));
		}
		
		Logger.log_INFO2("");
		Logger.log_INFO2("-- Répartition des catégories des sites :");
		Logger.log_INFO2(" Sur " + params.coucheSite.getNbAttributes() + " sites en entrée :");
		Logger.log_INFO2(params.coucheSite.getStatCategory(params.vCategorySite));
		Logger.log_INFO2("-- Répartition des catégories des segments :");
		Logger.log_INFO2(" Sur " + params.coucheSegment.getNbAttributes() + " Objets linéaires en entrée :");
		Logger.log_INFO2(params.coucheSegment.getStatCategory(params.vCategorySegment));

		Logger.log_INFO1("");
		Logger.log_INFO1("=== RAPPORT de performances ===");
		Logger.log_INFO2(UtilsTime.getStringDuree("Lecture fichier"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Optimisation segments"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Sous decoupage probleme"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Optimisation sites"));
		Logger.log_INFO1("=> DUREE TOTALE : " + UtilsCalcul.round(UtilsTime.getDuree("TOTAL PRE-TRAITEMENT DONNEES") + UtilsTime.getDuree("Lecture fichier"), 2));
		
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("");

	}

}
